package conditionsAPI;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

/**
 * Clase que permite generar una estructura de validadores. Los nodos hijo se 
 * agregan como precondiciones y cada vez que el estado de uno se modifica
 * recibe el evento onConditionStateUpdated.
 * Para actualizar el estado del validador llama a arePreConditionsFulfilled, este
 * metodo debe definirse en las clases hijo para dar comportamiento al nodo.
 * 
 * Las clases que hereden deben soportar dos tipos de comportamiento, uno serie
 * y otro paralelo. En el serie las condiciones deben cumplirse en el mismo
 * orden en que se cargan(idem a orden en preConditions), en el paralelo el orden 
 * no importa. Para saber en que modo debe actuar debe consultar conditionOrder.
 */
abstract class ComposedCondition extends Condition implements ConditionListener{

	protected ArrayList<Condition> preConditions;
	protected boolean conditionOrder;
	protected HashMap<String, Boolean> preConditionsOrder;
	
	public ComposedCondition(String id) throws ConditionDuplicatedException {
		super(id);
		this.preConditions = new ArrayList<Condition>();
		this.conditionOrder = false;
		this.preConditionsOrder = new HashMap<String, Boolean>();
	}
	
	public boolean isPreCondition(Condition condition){
		return this.preConditions.contains(condition);
	}
	
	public void enableConditionOrder(){
		this.conditionOrder = true;
		this.state = false;
		this.resetPreConditionsOrder();
	}
	
	public void disableConditionOrder(){
		this.conditionOrder = false;
		this.state = this.arePreConditionsFulfilled();
	}
	
	public boolean addPreCondition(Condition condition){
		if ((condition != this)&&(!this.isPreCondition(condition))){
			condition.addConditionListener(this);
			this.preConditions.add(condition);
			this.preConditionsOrder.put(condition.getId(), false);
			this.onConditionStateUpdated(condition);
			return true;
		}
		return false;
	}
	
	public boolean removePreCondition(Condition condition){
		if ((condition != this)&&(this.isPreCondition(condition))){
			condition.removeConditionListener(this);
			this.preConditions.remove(condition);
			this.preConditionsOrder.remove(condition.getId());
			this.onConditionStateUpdated(condition);
			return true;
		}
		return false;
	}
	
	@Override
	public void onConditionStateUpdated(Condition condition) {
		this.updateConditionState(condition);
		
		if (this.arePreConditionsFulfilled()){
			this.state = true;
			super.fireConditionChange(this);
		}
		else if (isPreCondition(condition)){
			this.state = false;
			super.fireConditionChange(condition);
		}
	}
	
	private void resetPreConditionsOrder(){
		Iterator<String> preConditionsIdOrderIterator = this.preConditionsOrder.keySet().iterator();
		while (preConditionsIdOrderIterator.hasNext()){
			String conditionId = preConditionsIdOrderIterator.next();
			preConditionsOrder.put(conditionId, false);
		}
	}
	
	/**
	 * Retorna siempre false. Como no es nodo hoja del arbol de condiciones solo 
	 * responde a eventos de otros nodos y no a los objetos de la coleccion.
	 */
	@Override
	public boolean checkConditionState(Object domainObject){
		return false;
	}
	
	/**
	 * Metodo que hay que implementar para actualizar el estado del validador 
	 * cada vez que cambia el estado de una de las precondiciones. 
	 * @param condition Precondicion que modifico su estado
	 */
	protected abstract void updateConditionState(Condition condition);
	
	/**
	 * Metodo que hay que implementar para determinar si las precondiciones
	 * se cumplen.
	 * @return boolean que indica el estado que deberia tener este validador
	 * considerando el valor de sus precondiciones.
	 */
	protected abstract boolean arePreConditionsFulfilled();
	
	/**
	 * Se redefine el metodo setState con una implementacion vacia, no deberia permitir
	 * cambiarse el estado de la condicion compuesta mediante el llamado a este metodo
	 */
	protected void setState(boolean conditionState){}
}
